package trabalho.das.fxml.views.controladores;

import estratégia.IMoverEstrategia;
import estratégia.MoverComRecursoTemplate;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import trabalho.das.fxml.views.uicomponents.CodeEditor;

/**
 * Trabalho DAS - 2014/2015
 *
 * @author a21210380, a21190325, a21210392
 */
public class ControladorCodeEditor implements Initializable {

    private JavaCompiler compiler;
    private StandardJavaFileManager fileManager;
    private final MyDiagnosticListener listener = new MyDiagnosticListener();

    @FXML
    private TextArea codeTextArea;

    @FXML
    private TextArea resultTextArea;

    @FXML
    private TextField classNameTextField;
    
    @FXML
    private ListView<Path> javaListView;
    
    @FXML
    private ListView<Path> classListView;
    
    private final CodeEditor codeEditor;

    public ControladorCodeEditor(CodeEditor codeEditor) {
        this.codeEditor = codeEditor;
    }
    
    @FXML
    private void onGuardarComoJavaPressionado(ActionEvent event) {
        if (classNameTextField.getText() == null || classNameTextField.getText().isEmpty()) {
            return;
        }
        File f = new File("estrategias/" + classNameTextField.getText() + ".java");
        if (!f.exists()) {
            try {
                if (!f.createNewFile()) {
                    System.out.println("Ficheiro estrategias/" + classNameTextField.getText() + ".java não foi criado!");
                    return;
                }
            } catch (IOException ex) {
                System.out.println(ex);
                return;
            }
        }
        try (PrintWriter pw = new PrintWriter(f)) {
            pw.print(codeTextArea.getText());
            codeEditor.loadJavaFiles(javaListView, codeTextArea);
        } catch (FileNotFoundException ex) {
            System.out.println(ex);
        }
    }
    
    @FXML
    private void onClearOutpuPressionado(ActionEvent event) {
        resultTextArea.clear();
    }
    
    @FXML
    private void onAdicionarEstratégiaPressionado(ActionEvent event) {
        if(classListView == null) {
            System.out.println("classListView == null");
            return;
        }
        Path selectedItem = classListView.getSelectionModel().getSelectedItem();
        if(selectedItem == null) {
            System.out.println("selectedItem == null");
            return;
        }
        String bigName = selectedItem.getFileName().toString();
        if(bigName == null || bigName.isEmpty() || bigName.length() <= 6) {
            System.out.println("bigName == null || bigName.isEmpty() || bigName.length() <= 6");
            return;
        }
        String nome = bigName.substring(0, bigName.length() - 6);
        Class load = load(nome);
        if(load.getSuperclass().equals(MoverComRecursoTemplate.class)) {
            try {
                sistema.Sistema.getInstance().setMoverEstrategia((MoverComRecursoTemplate)load.newInstance());
            } catch (InstantiationException | IllegalAccessException ex) {
                System.out.println(ex);
            }
        }
        
        Class[] declaredClasses = load.getInterfaces();
        for(Class s : declaredClasses) {
            if(s.equals(IMoverEstrategia.class)) {
                try {
                    sistema.Sistema.getInstance().setMoverEstrategia((MoverComRecursoTemplate)load.newInstance());
                } catch (InstantiationException | IllegalAccessException ex) {
                    System.out.println(ex);
                }
                return;
            }
        }
        System.out.println("sem interface IMoverEstrategia ou super classe MoverComRecursoTemplate");
    }

    @FXML
    private void onVerificarPressionado(ActionEvent event) {
        if (classNameTextField.getText() == null || classNameTextField.getText().isEmpty()) {
            return;
        }
        if (codeTextArea.getText() == null || codeTextArea.getText().isEmpty()) {
            return;
        }
        resultTextArea.clear();
        SimpleJavaFileObject java = getJavaFileContentsAsString(classNameTextField.getText(), codeTextArea.getText());
        if (java == null) {
            return;
        }

        if(compiler.getTask(null, fileManager, listener, null, null, Arrays.asList(java)).call()) {
            codeEditor.loadClassFiles(classListView);
        }
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        compiler = ToolProvider.getSystemJavaCompiler();
        fileManager = compiler.getStandardFileManager(listener, null, null);
        codeEditor.initialize(javaListView, classListView, codeTextArea);
    }
    
    class MyDiagnosticListener implements DiagnosticListener {

        @Override
        public void report(Diagnostic diagnostic) {
            StringBuilder sb = new StringBuilder();
            sb.append(resultTextArea.getText())
                    .append("Code->").append(diagnostic.getCode()).append('\n')
                    .append("Column Number->").append(diagnostic.getColumnNumber()).append('\n')
                    .append("End Position->").append(diagnostic.getEndPosition()).append('\n')
                    .append("Kind->").append(diagnostic.getKind()).append('\n')
                    .append("Line Number->").append(diagnostic.getLineNumber()).append('\n')
                    .append("Message->").append(diagnostic.getMessage(Locale.ENGLISH)).append('\n')
                    .append("Position->").append(diagnostic.getPosition()).append('\n')
                    .append("Source").append(diagnostic.getSource()).append('\n')
                    .append("Start Position->").append(diagnostic.getStartPosition()).append("\n\n");
            resultTextArea.setText(sb.toString());
        }
    }

    class JavaObjectFromString extends SimpleJavaFileObject {

        private String contents = null;

        public JavaObjectFromString(String className, String contents) throws Exception {
            super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.contents = contents;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
            return contents;
        }
    }

    private SimpleJavaFileObject getJavaFileContentsAsString(String className, String content) {
        try {
            return new JavaObjectFromString(className, content);
        } catch (Exception exception) {
            return null;
        }
    }

    private Class load(String className) {
        Path currentRelativePath = Paths.get("");

        try {
            URI url = currentRelativePath.toUri();
            URL[] urls = new URL[]{url.toURL()};
            ClassLoader cl = new URLClassLoader(urls);
            Class cls = cl.loadClass(className);
            return cls;
        } catch (MalformedURLException | ClassNotFoundException e) {
            System.out.println(e);
        }
        return null;
    }
}
